#include "config.h"

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include "configure.h"
#include "pool_proto.h"
#include "dbg.h"
#include "sysy_lib.h"
#include "pool_list.h"

#define POOL_LIST_INTERVAL 60     /* seconds */
static struct list_head __readpool__;
static sy_spinlock_t __readpool_lock__;

int pool_list_newfd(int *_fd)
{
        int ret, fd;
        char path[MAX_PATH_LEN];

        sprintf(path, "/dev/shm/lich4/l-XXXXXX");
        fd = mkstemp(path);
        if (fd < 0) {
                ret = -fd;
                DERROR("open %s error, errno:%d, errmsg:%s\n", path, ret, strerror(ret));
                GOTO(err_ret, ret);
        }

        ret = unlink(path);
        if (ret < 0) {
                ret = errno;
                GOTO(err_fd, ret);
        }

        *_fd = fd;
        
        return 0;
err_fd:
        close(fd);
err_ret:
        return ret;
}

int pool_list_addfd(const char *uuid, int fd)
{
        int ret;
        pool_list_t *fu;

        ret = ymalloc((void **)&fu, sizeof(*fu));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        fu->fd = fd;
        fu->create_time = gettime();
        strcpy(fu->uuid, uuid);

        ret = sy_spin_lock(&__readpool_lock__);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        list_add(&fu->hook, &__readpool__);

        sy_spin_unlock(&__readpool_lock__);
        
        return 0;
err_ret:
        return ret;
}

STATIC int __pool_list_delfd(const char *uuid, int *_fd)
{
        int ret, fd = -1;
        struct list_head *pos, *n;
        pool_list_t *fu;

        ret = sy_spin_lock(&__readpool_lock__);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        list_for_each_safe(pos, n, &__readpool__) {
                fu = (void *)pos;
                if (!strcmp(fu->uuid, uuid)) {
                        fd = fu->fd;

                        list_del(pos);
                        free(pos);

                        break;
                }
        }

        sy_spin_unlock(&__readpool_lock__);

        if (fd == -1) {
                ret = ENOENT;
                GOTO(err_ret, ret);
        }

        *_fd = fd;
        
        return 0;
err_ret:
        return ret;
}

STATIC int __pool_list_getfd(const char *uuid, int *_fd)
{
        int ret, fd = -1;
        struct list_head *pos, *n;
        pool_list_t *fu;

        ret = sy_spin_lock(&__readpool_lock__);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        list_for_each_safe(pos, n, &__readpool__) {
                fu = (void *)pos;
                if (!strcmp(fu->uuid, uuid)) {
                        fd = fu->fd;
                }
        }

        sy_spin_unlock(&__readpool_lock__);

        if (fd == -1) {
                ret = ENOENT;
                GOTO(err_ret, ret);
        }

        *_fd = fd;
        
        return 0;
err_ret:
        return ret;
}

int pool_list_lspool(const char *uuid, uint64_t offset, void *de, int *delen)
{
        int ret, fd;

        ret = __pool_list_getfd(uuid, &fd);
        if (ret) {
                if (ret == ENOENT) {
                        DERROR("%s not exist\n", uuid);
                        *delen = 0;
                        return 0;
                } else
                        GOTO(err_ret, ret);
        }

        ret = _pread(fd, de, *delen, offset);
        if (ret < 0) {
                ret = -ret;
                GOTO(err_ret, ret);
        }

        *delen = ret;

        return 0;
err_ret:
        return ret;
}

int pool_list_closefd(const char *uuid)
{
        int ret, fd = -1;

        ret = __pool_list_delfd(uuid, &fd);
        if (unlikely(ret)) {
                YASSERT(ret == ENOENT);
                goto out;
        }
        
        close(fd);
out:
        return 0;
}

STATIC void *__pool_list_watcher(void *arg)
{
        int ret;
        struct list_head *pos, *n;
        pool_list_t *fu;
        time_t now;

        (void) arg;
        
        while (srv_running) {
                now = gettime();

                ret = sy_spin_lock(&__readpool_lock__);
                if (unlikely(ret))
                        UNIMPLEMENTED(__DUMP__);
                
                list_for_each_safe(pos, n, &__readpool__) {
                        fu = (void *)pos;
                        if (now - fu->create_time > UUID_FILE_TIMEOUT) {
                                DWARN("uuid:%s, fd:%d create_time:%llu, timeout,"
                                      " next will close it.\n", fu->uuid, fu->fd, (LLU)fu->create_time);
                                close(fu->fd);
                                list_del(pos);
                                free(pos);
                        }
                }

                sy_spin_unlock(&__readpool_lock__);
                
                sleep(POOL_LIST_INTERVAL);
        }

        pthread_exit(NULL);
}

int pool_list_init()
{
        int ret;
        pthread_t th;
        pthread_attr_t ta;

        INIT_LIST_HEAD(&__readpool__);

        ret = sy_spin_init(&__readpool_lock__);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        (void) pthread_attr_init(&ta);
        (void) pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);

        ret = pthread_create(&th, &ta, __pool_list_watcher, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}
